<?php
// @formatter:off
/**
 * @file WCRule.php
 * @author Alejandro Dario Simi
 * @date $Date: 2013-08-20 00:23:06 +0000 (Tue, 20 Aug 2013) $
 *
 * $Id: WCRule.php 102 2013-08-20 00:23:06Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/branches/wcomix-1.0/includes/representations/WCRule.php $
 */
// @formatter:on

require_once "{$wcPaths["includes-directory"]}/WCRuleAction.php";
require_once "{$wcPaths["includes-directory"]}/search/WCSearch.php";

/**
 * @class WCRule This class represents entries saved in the table '%rules'.
 */
class WCRule extends WCGTableRepresentation implements WCSearchIndexable, WCPreviews {
	/**
	 * @var string Special character to use as separator in column
	 * rul_previews.
	 */
	public static $PreviewsSeparator = "|";
	/**
	 * @var int This constant holds the maximum length of column
	 * rul_previews.
	 */
	public static $PreviewsMaxLength = 640;
	/**
	 * @var string[] Check WCTableRepresentation class documentation.
	 */
	protected $_PropertiesBoolean = array(
		"conflict",
		"enabled",
		"indexed"
	);
	/**
	 * @var string Check WCTableRepresentation class documentation.
	 */
	protected $_PropertiesPrefix = "rul_";
	/**
	 * @var string Check WCTableRepresentation class documentation.
	 */
	protected $_PropertiesTable = "rules";
	/**
	 * @var WCRuleAction[int] This is the list of action that belongs to this
	 * rule.
	 */
	protected $_actions = array();
	//
	// Public methods.
	/**
	 * This method allows to get the list of actions of this rule.
	 *
	 * @return WCRuleAction[int] Returns a list of actions. If the current
	 * rule has issues, returns an empty list.
	 */
	public function actions() {
		//
		// Loading the list of actions.
		$this->loadActions();
		//
		// Returning the internal list of actions, unless there's a
		// problem with this rule.
		return $this->ok() ? $this->_actions : array();
	}
	/**
	 * This method allows to get the list of action's IDs of this rule.
	 *
	 * @return WCRuleAction[int] Returns a list of IDs. If the current
	 * rule has issues, returns an empty list.
	 */
	public function actionsIds() {
		//
		// Loading the list of actions.
		$this->loadActions();
		//
		// Returning a list of ID from the internal list of actions,
		// unless there's a problem with this rule.
		return $this->ok() ? array_keys($this->_actions) : array();
	}
	/**
	 * This method allows to know how many action belong to this rule grouped
	 * by their type.
	 *
	 * @return int[string] Returns an associative list of action counts.
	 */
	public function count() {
		//
		// Setting a default value to be returned.
		$out = array();
		//
		// Loading global pointers.
		global $wcRuleHooks;
		//
		// Starting the list to be returned with a counter for each action
		// type.
		foreach($wcRuleHooks as $id => $value) {
			$out[$id] = 0;
		}
		//
		// Loading the list of actions.
		$this->loadActions();
		//
		// Walking over all action and counting them depending on their
		// type.
		foreach($this->actions() as $action) {
			$out[$action->type()]++;
		}
		//
		// Returning the list of counts.
		return $out;
	}
	/**
	 * This method creates a new random previews list based on rules's
	 * actions, and sets it in column rul_previews.
	 *
	 * @return boolean Returns true if a previews list was generated.
	 */
	public function generatePreviews() {
		//
		// Setting a default value to be returned.
		$out = false;
		//
		// Loading global pointers.
		global $wcPageCache;
		//
		// Invalidating cached items related with this rule.
		$wcPageCache->invalidateByRule($this);
		//
		// Creating a temporary list for previews.
		$previewsFull = array();
		//
		// Loading previews of every rule action.
		foreach($this->actions() as $action) {
			$previewsFull = array_merge($previewsFull, $action->previews());
		}
		//
		// Calculation how many previews where loaded.
		$previewsCount = count($previewsFull);
		//
		// Checking if there are previews to set.
		if($previewsCount > 0) {
			//
			// Each rule can't have more than ten previews.
			$previewsCount = $previewsCount > 10 ? 10 : $previewsCount;
			//
			// Creating a list for previews to assign.
			$previews = array();
			//
			// Checking if there are more than one preview.
			if($previewsCount > 1) {
				//
				// Randomly selecting some previews.
				foreach(array_rand($previewsFull,$previewsCount) as $id) {
					$previews[] = $previewsFull[$id];
				}
			} else {
				//
				// Adding the only preview.
				$previews[] = $previewsFull[0];
			}
			//
			// Counting how many previews where chosen.
			$count = count($previews);
			//
			// Checking that there's something to assign.
			if($count && $count == $previewsCount) {
				//
				// Pasting and assigning previews.
				$this->previews = implode(self::$PreviewsSeparator, $previews);
				//
				// At this point, previews where generated and
				// assigned.
				$out = true;
			}
		}
		//
		// Returning generation result.
		return $out;
	}
	/**
	 * This method allows to know if this rule has a preview image.
	 *
	 * @return boolean Returns true when this rule has at least one preview.
	 */
	public function hasPreview() {
		//
		// This method depends on results from method 'hasPreviews()'.
		return $this->hasPreviews();
	}
	/**
	 * This method allows to know if this rule has a list of preview images.
	 *
	 * @return boolean Returns true when this rule has at least one preview.
	 */
	public function hasPreviews() {
		//
		// @warning Rules don't have a logic to manage preview yet.
		return false;
	}
	/**
	 * This method allows to know if this rule and its actions are indexed by
	 * the search engine.
	 *
	 * @param boolean $value When this parameter is present, this method acts
	 * as a setter for the index status on this rule and its actions.
	 * @return boolean Returns if this rule is considered to be indexed or not.
	 */
	public function indexed($value = null) {
		//
		// Checking if this method must act as setter.
		if($value !== null) {
			//
			// Setting this rule status.
			$this->indexed = $value;
			//
			// Setting rule actions status.
			foreach($this->actions() as $action) {
				$action->indexed($value);
			}
		}
		//
		// If this rule is indexed, its actions must be checked, perhaps
		// one of them is not.
		if($this->indexed) {
			//
			// Walking over each action.
			foreach($this->actions() as $action) {
				//
				// Checking if current action is indexed.
				if(!$action->indexed()) {
					//
					// At this point, one of the actions is
					// not indexed, therefore, this rule isn't
					// either.
					$this->indexed = false;
					//
					// One is enough.
					break;
				}
			}
		}
		//
		// Returning indexed status.
		return $this->indexed;
	}
	/**
	 * Loads a specific rule.
	 *
	 * @param int $rule Rule id to be loaded.
	 * @param WCGroup $group Group where the rule belongs.
	 */
	public function load($id, WCGroup &$group) {
		//
		// Calling the inherited constructor.
		parent::load($id, $group);
		//
		// Cleaning previews value.
		$this->_info["rul_previews"] = $this->_info["rul_previews"] ? $this->_info["rul_previews"] : false;
		//
		// Returning the final object status after loading.
		return $this->ok();
	}
	/**
	 * This method allows to get the thumbnail associated with current rule.
	 *
	 * @return string Returns an image URIs.
	 */
	public function preview() {
		//
		// Checking if there are previews set.
		if($this->previews) {
			//
			// Getting the list of possible thumbnails.
			$list = $this->previews();
			//
			// Returning the first URI from the list.
			return array_shift($list);
		} else {
			//
			// Where there is no preview, it returns an empty string.
			return "";
		}
	}
	/**
	 * This method allows to get the list of thumbnails associated with
	 * current rule.
	 *
	 * @return string[] Returns a list of image URIs.
	 */
	public function previews() {
		//
		// Checking if there are previews set.
		if($this->previews) {
			//
			// Splitting the contents of column rul_previews and
			// returning them as an array.
			return explode(self::$PreviewsSeparator, $this->previews);
		} else {
			//
			// Where there are no previews, it returns an empty array.
			return array();
		}
	}
	/**
	 * This method allows to reload this rule and its actions.
	 *
	 * @return boolean Returns true when the object was successfully reloaded.
	 */
	public function reload() {
		//
		// Clearing current actions.
		$this->_actions = array();
		//
		// Calling parent reloader.
		return parent::reload();
	}
	/**
	 * This method allows to remove current rule and all its actions.
	 *
	 * @return boolean Returns true when it was successfully removed.
	 */
	public function remove() {
		//
		// Walking over each action.
		foreach($this->actions() as $action) {
			//
			// Removing current action.
			$action->remove();
		}
		//
		// Calling parent remover.
		return parent::remove();
	}
	/**
	 * This methods allows to execute current rule running its actions.
	 *
	 * @param boolean $noConflics This flag forces action with conflicts to
	 * run.
	 * @return mixed[][] Returns an inform useful for cron tool
	 * 'ct.rules.run.php'.
	 */
	public function run($noConflics = false) {
		//
		// Creating a empty inform.
		$informs = array();
		//
		// Checking this rule's enabled.
		if($this->enabled) {
			//
			// This is a good opportunity to keep some statistics
			// updated.
			$this->count = count($this->actionsIds());
			//
			// Walking over each action.
			foreach($this->actions() as $action) {
				//
				// Setting action flags.
				$action->setIgnoreConfics(!$noConflics);
				//
				// Running action and adding its inform
				$informs[] = $action->run();
			}
		}
		//
		// Returning actions informs.
		return $informs;
	}
	/**
	 * This method allow to get a list of word that must be associated with
	 * this rule on a search engine.
	 *
	 * @return string[] Returns a list of words.
	 */
	public function words() {
		//
		// Creating a list of words and adding rule's name words.
		$out = WCSearchFactory::StringToPatterns($this->name);
		//
		// Adding each action name words to the list.
		foreach($this->actions() as $action) {
			$out = array_merge($out, $action->words());
		}
		//
		// Returning the list of words.
		return $out;
	}
	//
	// Protected methods.
	/**
	 * This method load the list of action associated with this rule.
	 *
	 * @todo use WCContainer or something alike.
	 */
	protected function loadActions() {
		//
		// Checking current rule status and amount of actions.
		if($this->ok() && !count($this->_actions)) {
			//
			// Clearing current action's list.
			$this->_actions = array();
			//
			// Creating a statement to load rule's actions.
			// @{
			$stmt = null;
			$stmtId = __CLASS__."::".__FUNCTION__;
			if($this->_db->has($stmtId)) {
				$stmt = $this->_db->get($stmtId);
			} else {
				$query = "select  *\n";
				$query .= "from    {$this->_dbprefix}rule_actions\n";
				$query .= "where   rel_ra_rule = :rule\n";
				$query .= "order by rel_ra_type asc\n";

				$stmt = $this->_db->prepare($stmtId, $query);
			}
			// @}
			//
			// Loading actions.
			if($stmt->execute(array(":rule" => $this->id))) {
				//
				// Loading each action with its proper class.
				foreach($stmt->fetchAll() as $row) {
					//
					// Asking factory for the right object.
					$action = WCRulesHolder::ActionFactory($row["rel_ra_type"], $this->id, $row["rel_ra_action"], $this->_group);
					//
					// If the action has no issues, it is
					// added.
					if($action->ok()) {
						$this->_actions[$action->id()] = $action;
					}
				}
			}
		}
	}
	//
	// Public class methods.
	/**
	 * This is a sorting method based on rule ids and it can be used on usort
	 * and uasort functions.
	 *
	 * @param WCRule $a Left operand.
	 * @param WCRule $b Right operand.
	 * @return boolean Returns true when the id of $a is greater than $b's.
	 */
	public static function IdSort($a, $b) {
		return $a->id > $b->id;
	}
	/**
	 * This is a sorting method based on comic names and it can be used on
	 * usort and uasort functions.
	 *
	 * @param WCRule $a Left operand.
	 * @param WCRule $b Right operand.
	 * @return boolean Returns true when the name of $a is greater than
	 * $b's.
	 */
	public static function NameSort($a, $b) {
		return $a->name > $b->name;
	}
	/**
	 * This is the general sorting method and it can be used on usort and
	 * uasort functions.
	 *
	 * @param WCRule $a Left operand.
	 * @param WCRule $b Right operand.
	 * @return boolean Returns true when $a goes after $b.
	 */
	public static function Sort($a, $b) {
		return self::NameSort($a, $b);
	}
}
?>